package com.haoxi.gateway.connector;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.haoxi.gateway.common.CommonMsgBean;

/**
 * TODO
 * 
 * Revision History
 * 
 * 2006-6-13,star.Zhao,created it
 */
public abstract class Connector {
    
    private static final Log log = LogFactory.getLog(Connector.class);
    protected Socket soc = null; // soc连接
    protected DataInputStream in = null; // 输入流
    protected DataOutputStream out = null; // 输出流
    protected boolean isReOpen = false; // 
    
    // private boolean keepLive = false;
    /**
     * 连接名
     */
    protected String connName;

    protected int maxConnCount;

    protected String protcolName;

    protected long lastSendTime;

    protected int socketTimeOut = 3 * 60 * 1000;

    protected long failConnectSleepTime = 5 * 1000;

    protected boolean isConnected;

    protected boolean isSocketKeepLive;

    protected int version;

    protected String gatewayName; // gateway name

    protected String ICP_ID; // enterprise code
    protected String loginName; // enterprise code
    protected String password; // pass word

    protected String gatewayIP;
    protected int gatewayPort;
    private static final Object syn_sequence_obj = new Object();
    private static int sequence;

    /**
     * get sequence
     * 
     * @param
     * @return sequence
     */
    public static int getSequence() {
        synchronized (syn_sequence_obj) {
            sequence++;
            if (sequence > 0x7fffffff)
                sequence = 0;
            return sequence;
        }
    }

    /**
     * 记录每一条连接器已经发出去，还没有收到一条的MTResp
     */
    private int current_SendMT_NoResp_Count = 0;
    private final int Max_SendMT_NoResp_Count = 30;
    private final Object SYN_NOMTRESP_COUNT_OBJ = new Object();

    /**
     * ++ SendMT_NoResp_Count 处理
     * 
     * @return
     */
    public void increaseSendMTNoRespCount() {
        synchronized (SYN_NOMTRESP_COUNT_OBJ) {
            current_SendMT_NoResp_Count++;
        }
    }

    /**
     * @return
     */
    public boolean isOverGreateMaxNoMTRespCount() {
        boolean bOK = false;
        if (current_SendMT_NoResp_Count >= Max_SendMT_NoResp_Count) {
            bOK = true;
        }
        return bOK;
    }

    /**
     * @return
     */
    public boolean isResultCirrentCount() {
        boolean bOK = false;
        if (current_SendMT_NoResp_Count == 0) {
            bOK = true;
        }
        return bOK;
    }

    /**
     * reset SendMT_NoResp_Count处理
     */
    public void resetSendMTNoRespCount() {
        synchronized (SYN_NOMTRESP_COUNT_OBJ) {
            current_SendMT_NoResp_Count = 0;
        }
    }

    /**
     * @param aGatewayName
     * @param connName
     * @param aICP_ID
     * @param loginName
     * @param aPassword
     * @param aGatewayIP
     * @param aGatewayPort
     * @param sVersion
     * @param protcolName
     */
    public Connector(String aGatewayName, String connName, String aICP_ID,
            String loginName, String aPassword, String aGatewayIP,
            int aGatewayPort, int sVersion, String protcolName) {
        this.gatewayName = aGatewayName;
        this.connName = connName;
        this.ICP_ID = aICP_ID;
        this.loginName = loginName;
        this.password = aPassword;
        this.gatewayIP = aGatewayIP;
        this.gatewayPort = aGatewayPort;
        this.version = sVersion;
        this.protcolName = protcolName;
        this.isConnected = false;
        this.isSocketKeepLive = false;
    }

    /**
     * re Open connect
     * 
     * @return
     */
    public boolean reOpenConnect() {
        boolean bOK = false;
        isReOpen = true;   // 表示重新连接
        disConnected();
        resetSendMTNoRespCount();
        try {
            Thread.sleep(failConnectSleepTime);
        } catch (InterruptedException e) {
            log.debug("", e);
        }
        
        bOK = connect();
        // 如果连接成功,
        synchronized (this) {
            if (bOK && !isClose()) {
                this.notifyAll();
            }
        }
        isReOpen = false;   // 重新连接完毕
        return isConnected;
    }

    /**
     * get connect
     * 
     * @return
     */
    public boolean openConnect() {
        return isConnected = connect();
    }

    /**
     * conect to CMCC
     */
    protected synchronized boolean connect() {
        StringBuffer logStrBuff = new StringBuffer();
        int connTimes = 0;
        String gatewayhead = gatewayName + " " + protcolName + " ";

        try {
            logStrBuff.append("start connect to ");
            logStrBuff.append(gatewayhead);
            log.debug(logStrBuff.toString());
            log.debug(gatewayhead + " ip:" + gatewayIP);
            log.debug(gatewayhead + " port:" + gatewayPort);
            soc = new Socket(gatewayIP, gatewayPort);
            log.debug("Socket build OK !!!");
            soc.setSoTimeout(socketTimeOut);
            in = new DataInputStream(new BufferedInputStream(soc
                    .getInputStream()));
            out = new DataOutputStream(new BufferedOutputStream(soc
                    .getOutputStream()));
            isSocketKeepLive = true;
            log.debug(gatewayhead + " icpID:" + ICP_ID);
            log.debug(gatewayhead + " password:" + password);
            log.debug(gatewayhead + " version:" + version);
            while (!isConnected) {
                if (!soc.isConnected()) {
                    isSocketKeepLive = false;
                    log.error("Socket is close, login on SMG failure !!!");
                    log.debug(gatewayhead + " connect failure!");
                    break;
                }

                try {
                    connTimes++;
                    if (connTimes > 5) {
                        log
                                .debug(gatewayhead
                                        + " connections too much failue,the program is Exit");
                        connTimes = 0;
                        // if 15 times not connect, the program is exit
                        System.exit(1);
                    }
                    isConnected = checkConnect(loginName, password);
                    log.debug(gatewayhead + " connections times is:"
                            + connTimes);
                    if (isConnected) {
                        log.debug(gatewayhead + " connect ok!");
                        break;
                    }
                } catch (Exception e) {
                    isConnected = false;
                    log.error(gatewayhead + " connect", e);
                } finally {
                    if (!isConnected) {
                        // 如果连接不成功，sleep一段时间，继续连接
                        try {
                            Thread.sleep(failConnectSleepTime);
                        } catch (InterruptedException e) {
                            log.debug(gatewayhead + " sleep:", e);
                        }
                    }
                }
            }

        } catch (Exception e) {
            isConnected = false;
            log.error(gatewayhead + "find a Exception:", e);
        }
        return isConnected;
    }

    /**
     * abstract login to gateway
     * 
     * @return success or fales
     */
    abstract public boolean checkConnect(String loginName, String password);

    /**
     * read a protcol
     * 
     * @return protcol object
     */
    abstract public CommonMsgBean readProtcol() throws IOException;

    /**
     * write a protcol
     * 
     * @param obj
     * @return true or false
     */
    abstract public boolean writeProtcol(CommonMsgBean obj) throws IOException;

    /**
     * write to socket output
     * 
     * @param b
     * @return
     */
    protected boolean writeToOut(byte[] b) throws IOException {
        boolean bOK = false;
        try {
            // if connect send it
            if (!isClose()) {
                out.write(b);
                out.flush();
                lastSendTime = System.currentTimeMillis();
                bOK = true;
            }else{
                log.error(connName + " write failure !!!");
            }
        } catch (IOException e) {
            log.error("write error:", e);
            isConnected = false;
            throw new IOException();
        }
        return bOK;
    }

    /**
     * @return Returns the gatewayPort.
     */
    public int getGatewayPort() {
        return gatewayPort;
    }

    public long getLastSendTime() {
        return lastSendTime;
    }

    /**
     * dis connection
     */
    public synchronized void disConnected() {
        this.isConnected = false;
        this.isSocketKeepLive = false;
        String gatewayhead = gatewayName + " " + protcolName + " ";
        try {
            if (this.in != null) {
                this.in.close();
                //Thread.sleep(200);
            }
        } catch (Exception e) {
            log.error(gatewayhead + "has a exception at input close:", e);
        } finally {
            this.in = null;
        }
        try {
            if (this.out != null) {
                this.out.close();
                //Thread.sleep(200);
            }
        } catch (Exception e) {
            log.error(gatewayhead + "has a exception at output close:", e);
        } finally {
            this.out = null;
        }
        try {
            if (this.soc != null) {
                this.soc.close();
                //Thread.sleep(200);
            }
        } catch (Exception e) {
            log.error(gatewayhead + "has a exception at socket close:", e);
        } finally {
            this.soc = null;
        }
    }

    /**
     * @return Returns the connName.
     */
    public String getConnName() {
        return connName;
    }

    /**
     * check socket connection
     * 
     * @return
     */
    public boolean isClose() {
//        log.error("curent connector's socket state : !soc.isClosed() [" + !soc.isClosed()+"]");
//        log.error("curent connector's connect state : isConnected [" + isConnected+"]");
        if (soc != null && isConnected) {
            isConnected = !soc.isClosed();
        } else {
            isConnected = false;
        }
        return !isConnected;
    }

    /**
     * @param soc The soc to set.
     * @throws IOException
     */
    public synchronized void setSocInfo(Socket soc) {
        if (null == soc) {
            return;
        }

        try {
            this.soc = soc;
            in = new DataInputStream(new BufferedInputStream(soc
                    .getInputStream()));
            this.out = new DataOutputStream(new BufferedOutputStream(soc
                    .getOutputStream()));
            isSocketKeepLive = true;
            this.notifyAll();
        } catch (IOException e) {
            isSocketKeepLive = false;
            log.error("IOException in Connector.setSocInfo()", e);
        }
    }

    /**
     * @param maxConnCount The maxConnCount to set.
     */
    public void setMaxConnCount(int maxConnCount) {
        this.maxConnCount = maxConnCount;
    }

    /**
     * @return Returns the isSocketKeepLive.
     */
    public boolean isSocketKeepLive() {
        return isSocketKeepLive;
    }

    /**
     * @return Returns the loginName.
     */
    public String getLoginName() {
        return loginName;
    }

    /**
     * @return Returns the password.
     */
    public String getPassword() {
        return password;
    }

    /**
     * @return Returns the version.
     */
    public int getVersion() {
        return version;
    }
    public void setSocketKeepLive(boolean keepLive) {
        this.isSocketKeepLive=keepLive;
    }
}
